Abraxus's Blog

ImaginaryCTF Jumprope Write Up

Details:

Jeopardy style CTF

Category: Reverse Engineering

Points: 200

Comments:

CENSORED and CENSORED Sitting in a tree, H-A-C-K-I-N-G! First comes pwn, Then comes rev, Then comes a flag And a happy dev!

Write up:

Opening the main function I saw:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  puts("Ice cream!");
  puts("Soda Pop!");
  puts("Cherry on top!");
  puts("Is your flag exact?");
  puts("Well, let's find out!");
  sleep(1u);
  puts("\nEighty-eight characters!");
  puts("A secret well kept!");
  puts("If you get it right,");
  puts("I'll shout CORRECT!\n");
  if ( !(unsigned int)checkFlag("I'll shout CORRECT!\n", argv) )
    printf("Nope!");
  return 0;
}

I then looked into the checkFlag function and saw:

__int64 checkFlag()
{
  char vars0[8]; 
  void *retaddr; 

  printf(">>> ");
  __isoc99_scanf("%88s%c", &retaddr, &dead);
  for ( count = 8; count <= 95; ++count )
  {
    val = next(val);
    vars0[count] ^= x[count - 8] ^ val;
  }
  return 0LL;
}

From here I can see that the code reads our input and then we xor that data with some other xor'ed data.

From here I extracted the next function:

unsigned __int64 __fastcall next(unsigned __int64 a1)
{
  int j; 
  unsigned __int64 v4; 
  __int64 v5; 
  int i; 

  for ( i = 0; i <= 7; ++i )
  {
    v5 = 0LL;
    v4 = a1;
    for ( j = 0; j <= 7; ++j )
    {
      if ( (unsigned int)test((unsigned int)(j + 1)) )
        v5 ^= v4 & 1;
      v4 >>= 1;
    }
    a1 = (v5 << 7) + (a1 >> 1);
  }
  return a1;
}

The test function:

__int64 __fastcall test(int a1)
{
  int i; // [rsp+10h] [rbp-4h]

  if ( a1 == 1 )
    return 0LL;
  for ( i = 2; i < a1 - 1; ++i )
  {
    if ( !(a1 % i) )
      return 0LL;
  }
  return 1LL;
}

And the x array:

[0x00000000000000FD, 0x000000000000003C, 0x00000000000000C4, 0x000000000000000E, 0x0000000000000076, 0x00000000000000FF, 0x000000000000004B, 0x0000000000000045, 0x000000000000001F, 0x0000000000000040, 0x00000000000000F4, 0x00000000000000E6, 0x0000000000000080, 0x00000000000000B8, 0x00000000000000B5, 0x00000000000000E8, 0x0000000000000076, 0x000000000000008E, 0x000000000000003B, 0x00000000000000F8, 0x00000000000000E4, 0x00000000000000BD, 0x00000000000000C9, 0x00000000000000C7, 0x000000000000003F, 0x00000000000000E6, 0x00000000000000CF, 0x0000000000000015, 0x0000000000000094, 0x000000000000009A, 0x000000000000008A, 0x0000000000000028, 0x000000000000004E, 0x000000000000005E, 0x000000000000001E, 0x000000000000003F, 0x0000000000000025, 0x00000000000000D4, 0x000000000000002C, 0x00000000000000A9, 0x0000000000000036, 0x0000000000000028, 0x0000000000000042, 0x0000000000000040, 0x0000000000000093, 0x000000000000008D, 0x000000000000000F, 0x00000000000000FF, 0x00000000000000AE, 0x000000000000002B, 0x000000000000002B, 0x00000000000000DF, 0x000000000000007E, 0x000000000000001A, 0x000000000000004E, 0x0000000000000005, 0x0000000000000063, 0x00000000000000D0, 0x0000000000000088, 0x00000000000000E1, 0x00000000000000A1, 0x000000000000001F, 0x000000000000005A, 0x000000000000003D, 0x0000000000000036, 0x000000000000004F, 0x00000000000000AE, 0x0000000000000089, 0x000000000000007B, 0x00000000000000D7, 0x0000000000000027, 0x00000000000000D0, 0x0000000000000029, 0x00000000000000C0, 0x000000000000009E, 0x00000000000000F0, 0x0000000000000020, 0x00000000000000DF, 0x0000000000000069, 0x0000000000000077, 0x0000000000000094, 0x00000000000000E9, 0x0000000000000058, 0x000000000000000F, 0x00000000000000B8, 0x00000000000000EC, 0x00000000000000F9, 0x0000000000000024]

I then made a python script to find everything but the input key:

x = [0x00000000000000FD, 0x000000000000003C, 0x00000000000000C4, 0x000000000000000E, 0x0000000000000076, 0x00000000000000FF, 0x000000000000004B, 0x0000000000000045, 0x000000000000001F, 0x0000000000000040, 0x00000000000000F4, 0x00000000000000E6, 0x0000000000000080, 0x00000000000000B8, 0x00000000000000B5, 0x00000000000000E8, 0x0000000000000076, 0x000000000000008E, 0x000000000000003B, 0x00000000000000F8, 0x00000000000000E4, 0x00000000000000BD, 0x00000000000000C9, 0x00000000000000C7, 0x000000000000003F, 0x00000000000000E6, 0x00000000000000CF, 0x0000000000000015, 0x0000000000000094, 0x000000000000009A, 0x000000000000008A, 0x0000000000000028, 0x000000000000004E, 0x000000000000005E, 0x000000000000001E, 0x000000000000003F, 0x0000000000000025, 0x00000000000000D4, 0x000000000000002C, 0x00000000000000A9, 0x0000000000000036, 0x0000000000000028, 0x0000000000000042, 0x0000000000000040, 0x0000000000000093, 0x000000000000008D, 0x000000000000000F, 0x00000000000000FF, 0x00000000000000AE, 0x000000000000002B, 0x000000000000002B, 0x00000000000000DF, 0x000000000000007E, 0x000000000000001A, 0x000000000000004E, 0x0000000000000005, 0x0000000000000063, 0x00000000000000D0, 0x0000000000000088, 0x00000000000000E1, 0x00000000000000A1, 0x000000000000001F, 0x000000000000005A, 0x000000000000003D, 0x0000000000000036, 0x000000000000004F, 0x00000000000000AE, 0x0000000000000089, 0x000000000000007B, 0x00000000000000D7, 0x0000000000000027, 0x00000000000000D0, 0x0000000000000029, 0x00000000000000C0, 0x000000000000009E, 0x00000000000000F0, 0x0000000000000020, 0x00000000000000DF, 0x0000000000000069, 0x0000000000000077, 0x0000000000000094, 0x00000000000000E9, 0x0000000000000058, 0x000000000000000F, 0x00000000000000B8, 0x00000000000000EC, 0x00000000000000F9, 0x0000000000000024]

val = 2

def test(b):
	if b == 1:
		return 0
	for i in range(2, b-1):
		if not (b%i):
			return 0
	return 1

def next(a):
	for i in range(0, 8):
		v5 = 0
		v4 = a
		for j in range(0, 8):
			if test(j+1):
				v5 ^= v4 & 1
			v4 >>= 1
		a = (v5 << 7) + (a >> 1)
	return a

def printA(s):
	n = ""
	for i in s:
		n+=i
	print(n)

s = []

for i in range(8, 96):
	val = next(val)
	s.append(chr(x[i-8] ^ val))
printA(s)

This then output:

xq4f{n0tÔx!st_ni´¨CLbut_Zz%_nigh/M"ef0ref7enty_"5r_haczw2s_camîK!_kn0c¥Ã¹_at_økd00r}

From here it was a mix or bruteforcing a guessing but we were able to find that the flag was:

ictf{n0t_last_night_but_the_night_bef0re_twenty_f0ur_hackers_came_a_kn0cking_at_my_d00r}